home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre2.z / postgre2 / doc / implementation / planner / 3.design < prev    next >
Encoding:
Text File  |  1992-08-27  |  48.5 KB  |  1,248 lines

  1. .sh 1 "DESIGN OF THE OPTIMIZER" 3
  2. .pp
  3. This section will first describe the optimization algorithm chosen
  4. for POSTGRES, focusing on features specifically incorporated to handle
  5. extendibility in POSTGRES.
  6. The rest of the section will then indicate how this algorithm 
  7. is used in optimizing nested-attribute queries.
  8. Algorithms are described in high-level detail with special attention given
  9. to the design rationale behind various features.
  10. Plans produced by these algorithms are also described to indicate
  11. how the query processor interprets optimizer plans.
  12. .sh 2 "Choice of Optimization Algorithm"
  13. .pp
  14. In selecting an optimization algorithm to work with, there were two choices \*-
  15. query decomposition [WONG76] or exhaustive search.
  16. Query decomposition is a heuristic \*(lqgreedy\*(rq algorithm that
  17. proceeds in a stepwise fashion.
  18. If a query has three or more variables, heuristics are first
  19. used to subdivide the query into two smaller subqueries.
  20. This process is applied recursively to any subqueries that contain
  21. at least three variables.
  22. For subqueries containing less than three variables, tuple substitution is
  23. used to process the join, while a component known as the one-variable query
  24. processor determines the path used to scan individual relations.
  25. .pp
  26. Once the first step of a query plan has been constructed using this
  27. decomposition algorithm, the step is executed.
  28. By doing this, the optimizer has information on the sizes of
  29. intermediate results,
  30. which can be used to its advantage in making subsequent decisions.
  31. Furthermore, the search space is reduced substantially because only a
  32. single path is considered.
  33. However, as a result, potentially good plans
  34. are ignored during early stages of the algorithm.
  35. .pp
  36. The SYSTEM-R designers took a dramatically 
  37. different approach by essentially doing an exhaustive search of 
  38. the plan space.
  39. All possible ways of scanning each relation appearing within a query are
  40. found.
  41. Using these paths, all plans for processing two-way joins are considered.
  42. Then, single relations are joined to form three-way joins, and
  43. from here, the algorithm iterates in a similar manner.
  44. The cost of executing each of these paths is estimated, and the cheapest is
  45. selected as the desired plan.
  46. .pp
  47. Although exhaustive search inevitably requires more planning time,
  48. good plans are not overlooked.
  49. This is especially important when optimizing complicated queries
  50. because for these queries the difference in the amount of processing 
  51. required by two plans can be quite significant.
  52. Thus, the extra planning overhead is more than compensated
  53. by savings that result from executing a better plan.
  54. For simple queries, although the selected plan may not be
  55. significantly better than 
  56. another, the extra overhead is likely to be inconsequential.
  57. For queries embedded within data fields, the extra overhead of enumerative
  58. planning is especially unimportant because these queries will be
  59. preexecuted in background mode and POSTGRES will cache
  60. the execution results as well as the compiled query plans.
  61. In these cases, the time
  62. spent optimizing will be amortized over several executions.
  63. .pp
  64. The SYSTEM-R optimizer only considers linear joins, e.g.,
  65. .(l
  66. ((A join B) join C) join D
  67. .)l
  68. for a 4-way join.
  69. The optimizer could be improved to consider joins between pairs of
  70. composite relations, e.g.,
  71. .(l
  72. (A join B) join (C join D).
  73. .)l
  74. This would allow the optimizer to examine further plans, and on occasion,
  75. these plans may be
  76. significantly better than plans that only utilize linear joins.
  77. .pp
  78. Another enhancement to the SYSTEM-R optimizer is to consider plans that
  79. will dynamically create indices
  80. on join attributes if they are not already available.
  81. If the cost of building the index is small compared to the savings that
  82. result from using the index in a join, such a strategy can be advantageous.
  83. .pp
  84. The POSTGRES optimizer does not consider these enhancements either.
  85. Although they would undoubtedly result in a better optimizer, 
  86. the main goal of POSTGRES is to illustrate the feasibility of the novel 
  87. features that it will support.
  88. Therefore, for simplicity, the POSTGRES optimizer will adhere fairly closely 
  89. to the algorithm as described in [SELI79].
  90. .sh 2 "Pipelining of Tuples"
  91. .pp
  92. Ignoring nested attributes for the moment, the query plan created by the 
  93. optimizer is a tree of scan and join nodes.
  94. Relations are either scanned sequentially or via primary or secondary
  95. indices.
  96. Joins are processed using nested iteration, merge-sorts, or hash-joins.
  97. Each of these join strategies will be discussed further in section 3.3.3.
  98. .pp
  99. Every join involves two components.
  100. The component that is scanned first will be referred from hereon as the
  101. \*(lqouter join relation,\*(rq while the other component is
  102. the \*(lqinner join relation.\*(rq
  103. For a query containing \fIn\fR variables, the plan
  104. is constructed in such a way that the \fIn\fR-way composite join
  105. appears at the top of the tree. 
  106. (See figure 3.1.)
  107. .(z
  108. .hl
  109. .GS
  110. file figure3.1
  111. .GE
  112. .sp
  113. .ce
  114. .sz \n(ep
  115. (((A1 join A2) join A3) join ... A(n-1)) join An
  116. .sz \n(pp
  117. .sp
  118. .ce 2
  119. \fBFigure 3.1\fR
  120. Plan tree for an \fIn\fR-way join
  121. .hl
  122. .)z
  123. The left subtree is a plan
  124. for processing the outer join relation, and the right subtree corresponds
  125. to a plan for processing the inner join relation.
  126. Because the optimizer only considers linear joins (see section 3.1),
  127. the right subtree is always a scan
  128. node while the left subtree is a plan for an \fI(n-1)\fR-way join, or
  129. scan if \fIn\fR = 2.
  130. These characteristics apply recursively to the rest of the tree.
  131. .pp
  132. To process such a plan, the query processor walks through the tree starting
  133. at the root.
  134. If the node is a join, depending on the join strategy, calls are made on
  135. either the left or right subtrees to retrieve tuples from either the
  136. outer or inner join relations.
  137. If the node is a scan node, the appropriate relation is scanned using
  138. the selected strategy.
  139. When scanning a relation, restriction clauses specified on the relation
  140. are examined.
  141. Once a single tuple has been found satisfying these qualifications, the
  142. tuple is returned to the node that initiated the scan.
  143. If the higher node was a join node and this tuple originated from the
  144. left subtree of the join node, then a call is made on the right subtree.
  145. Otherwise, this tuple originated from the right subtree and thus can be
  146. joined with the tuple passed back by the left subtree.
  147. Provided
  148. all corresponding join clauses are satisfied, a composite tuple is formed.
  149. If the join clauses are not satisfied, calls are made on either the right
  150. or left subtrees until a qualifying composite tuple can be constructed.
  151. Once this composite tuple is formed, it is passed upward to the node that called
  152. this join node, and the process is repeated.
  153. .pp
  154. If tuples must be sorted or hashed prior to join processing (see figure 3.2),
  155. all tuples returned from a lower node must first be stored in a temporary
  156. relation.
  157. .(z
  158. .hl
  159. .GS
  160. file figure3.2
  161. .GE
  162. .sp
  163. .ce 2
  164. \fBFigure 3.2\fR
  165. Sort node in a plan tree
  166. .hl
  167. .)z
  168. Once the lower node has passed all relevant tuples into the temporary,
  169. the sort or hash is performed.
  170. From here, the temporary relation is scanned like any other relation,
  171. and its tuples are also pipelined upward.
  172. In summary, 
  173. calls are made on lower nodes in the tree when tuples are needed at higher nodes
  174. to continue processing, and
  175. tuples originating from scan nodes at the leaves of the
  176. plan tree are pipelined bottom-up to form composite tuples, which also are
  177. pipelined upward.
  178. .pp
  179. As an alternative to pipelining, the query executor could have processed
  180. nodes to completion,
  181. stored the intermediate subresults in temporary relations,
  182. and then passed groups of 
  183. tuples upwards rather than a tuple at a time.
  184. This may be advantageous when there are many duplicate tuples in a subresult.
  185. The duplicates could be removed from the temporary, reducing the time
  186. required to process later joins.
  187. However, for simplicity, we chose to focus on a pipeline processing scheme for
  188. now.
  189. Implementation of temporaries will be reserved for future extensions.
  190. .pp
  191. .sh 2 "Generating Possible Plans"
  192. .pp
  193. The SYSTEM-R optimizer decides which plans should be generated
  194. based upon 
  195. the types of indices defined on relations appearing in a query as well
  196. operators that also appear in the query.
  197. For example, suppose a B-tree index is defined on a relation, and 
  198. a query contains the following restriction clause:
  199. .(l
  200. \fIrelation.field OPR constant\fR.
  201. .)l
  202. Using Selinger's notation, clauses of this form will be referred from
  203. hereon as \*(lqsargable\*(rq clauses [SELI79].
  204. If \fIrelation.field\fR within a sargable clause happens to match the
  205. key of the B-tree index and
  206. the restriction operator, \fIOPR\fR, is anything but
  207. $!=$, then an index path should be considered because a B-tree provides
  208. efficient access when used with the following operators:
  209. .(l
  210. =, <, $<=$, >, $>=$.
  211. .)l
  212. The criteria for decisions like this can easily be
  213. incorporated into the SYSTEM-R optimization code
  214. because a conventional database only has a fixed number of operators
  215. and access methods;
  216. so there are only a fixed number of possibilities.
  217. Clearly the POSTGRES optimizer cannot use such a strategy due to 
  218. POSTGRES's provision for extendibility.
  219. Therefore, we resorted to storing operator and access method characteristics
  220. in database system catalogs, and we coded the optimizer to access and use
  221. this information in generating possible plans.
  222. The rest of this subsection will discuss in greater detail the steps
  223. taken in creating access plans in order to focus on the type of information
  224. the optimizer and other relevant parts of the system will need in 
  225. making decisions.
  226. .sh 3 "Transforming Queries Into Standard Form"
  227. .pp
  228. Prior to optimizing a query, the parser must take the user's ascii request,
  229. parse it for valid syntax, perform semantic checks,
  230. and finally generate a tree representing information within the query.
  231. To make optimization simpler and to produce more efficient
  232. query plans, the parser must place the qualification portion of every
  233. query in conjunctive normal form.
  234. This entails pushing all \*(lqor\*(rq clauses to the innermost levels
  235. of a qualification using the following distributive rule:
  236. .(l
  237. a or ( b and c ) $==$ ( a or b ) and ( a or c ).
  238. .)l
  239. The optimizer also requires that \*(lqnot's\*(rq be pushed to the innermost
  240. levels using DeMorgan's law:
  241. .(l
  242. not ( a and b ) $==$ not ( a ) or not ( b )
  243. not ( a or b ) $==$ not ( a ) and not ( b )
  244. .)l
  245. and if possible, removed from the query.
  246. For example, the qualification in figure 3.3 is equivalent to the 
  247. qualification in figure 3.5, which is in conjunctive normal form with
  248. \*(lqnot's\*(rq removed.
  249. .(z
  250. .hl
  251. .(l C
  252. .sz \n(ep
  253. not ( r.f = 1 ) or ( not ( r.f2 > 1 or r.f2 < 3 ))
  254. .sz \n(pp
  255. .sp
  256. \fBFigure 3.3\fR
  257. Qualification in non-standard form
  258. .sp 2
  259. .sz \n(ep
  260. ( not ( r.f = 1 ) or not ( r.f2 > 1 )) and ( not ( r.f = 1 ) or not ( r.f2 < 3 ))
  261. .sz \n(pp
  262. .sp
  263. \fBFigure 3.4\fR
  264. Equivalent qualification in conjunctive normal form
  265. .sp 2
  266. .sz \n(ep
  267. ( r.f $!=$ 1 or r.f2 $<=$ 1 ) and ( r.f $!=$ 1 or r.f2 $>=$ 3 )
  268. .sz \n(pp
  269. .sp
  270. \fBFigure 3.5\fR
  271. Equivalent qualification in conjunctive normal form with \*(lqnot's\*(rq removed
  272. .)l
  273. .hl
  274. .)z
  275. .pp
  276. Removing \*(lqnot's\*(rq from a qualification
  277. requires substituting operators with their respective negations.
  278. For example, \*(lq=\*(rq would be replaced by \*(lq$!=$,\*(rq while
  279. \*(lqAREAGT\*(rq would be replaced by \*(lqAREALE.\*(rq
  280. For the parser to make these substitutions,
  281. users must specify an operator's
  282. corresponding negation, in addition to other information, when defining
  283. a new operator.
  284. The information is specified as follows:
  285. .(l
  286. \fBdefine operator\fR (\fBopname is\fR =, $...$, \fBnegator is\fR $!=$, $...$)
  287. .)l
  288. and is stored in an operator catalog, accessible by
  289. the parser.
  290. .pp
  291. There are, however, problems associated with this requirement.
  292. First of all, this \fIforces\fR users to define operators
  293. corresponding to negators.
  294. In other words, having specified \*(lqAREANEQ\*(rq as a negator, it is also
  295. necessary to define an operator called \*(lqAREANEQ.\*(rq
  296. Although this definition is not difficult, since a negator is 
  297. the logical opposite of an already defined operator, users may have
  298. no need for the negator, and therefore would rather not have defined
  299. the extraneous operator.
  300. Secondly, because every negator is also a user-defined operator, an
  301. operator's negator may be specified before the negation operator has been
  302. defined.
  303. In other words, depending on whether the operator \*(lqAREAEQ\*(rq
  304. or \*(lqAREANEQ\*(rq
  305. is defined first, the other operator will not have been defined yet
  306. when the negator of the first is specified.
  307. This means there is no guarantee that the specified
  308. negator is actually a valid operator;
  309. a user could specify the negator of \*(lqAREAEQ\*(rq to be \*(lqfoobar,\*(rq
  310. or he may specify the correct negator, \*(lqAREANEQ,\*(rq but forget to
  311. define the \fIoperator\fR \*(lqAREANEQ.\*(rq
  312. .pp
  313. We have addressed both issues by implementing the optimizer so it allows
  314. \*(lqnot's\*(rq to appear within qualifications.
  315. Therefore, if a user defines a negator that happens to be a nonexistent
  316. operator (e.g., \*(lqfoobar\*(rq) or doesn't specify one,
  317. the parser has no choice but to push the \*(lqnot's\*(rq as far as possible
  318. into the qualification without actually removing them.
  319. (See figure 3.4.)
  320. The only problem with this is that the optimizer may not produce 
  321. optimal plans when \*(lqnot's\*(rq are left within clauses.
  322. For example, the following query:
  323. .(l
  324. (1) \fBretrieve\fR (foo.bar) \fBwhere not\fR(foo.f1 AREANEQ 1)
  325. .)l
  326. is equivalent to this query:
  327. .(l
  328. (2)  \fBretrieve\fR (foo.bar) \fBwhere\fR foo.f1 AREAEQ 1
  329. .)l
  330. because \fBnot\fR(AREANEQ) $==$ AREAEQ.
  331. If an area B-tree index is defined on the field \*(lqf1,\*(rq
  332. then the optimizer would definitely consider an index scan because the operator,
  333. \*(lqAREAEQ,\*(rq in query (2) can be used with an area B-tree.
  334. However, if a user had not specified the negation of \*(lqAREANEQ,\*(rq
  335. then the transformation from (1) to (2) would not have been possible,
  336. and the optimizer could not have considered an index scan.
  337. In this case, the index scan probably
  338. would have been the optimal path.
  339. Therefore, it is to the user's advantage to specify and define negators.
  340. .pp
  341. Another desirable transformation is that variables in
  342. qualifications appear on the left-hand side of an operator and constants
  343. on the right (e.g., \fIr.field OPR constant\fR).
  344. To make this transformation, operands must be reversed and operators
  345. must be replaced with their respective
  346. \*(lqcommutators,\*(rq which again the user must specify.
  347. For example, the commutator of \*(lq<\*(rq is \*(lq>,\*(rq while the
  348. commutator of \*(lqAREAEQ\*(rq is also \*(lqAREAEQ.\*(rq
  349. The issues and solution discussed in the previous paragraphs in
  350. reference to negators also apply here, and
  351. again, it is to the user's advantage to specify commutators.
  352. The reasoning behind this will be discussed further in section 3.3.2.
  353. Basically, it enables the optimizer to consider index paths
  354. it could not have considered had a variable appeared on the right hand
  355. side of an operator.
  356. .sh 3 "Index Scans"
  357. .pp
  358. Once a query tree has been transformed as close as possible to standard form,
  359. it is passed to the optimizer.
  360. The first step the optimizer takes is to find all feasible paths for
  361. scanning each relation appearing in the query.
  362. Relations can always be scanned sequentially;
  363. therefore, a sequential scan path is always considered.
  364. Generally when sargable clauses (i.e., clauses of the form
  365. \fIrelation.field OPR constant\fR)
  366. appear within queries,
  367. indices will restrict the amount of search required.
  368. Therefore, if a user has a primary index or secondary indices
  369. defined on a relation, all viable index paths are also considered.
  370. .pp
  371. For an index to be considered, its keys must match variables
  372. that appear within sargable restrictions.
  373. The optimizer also needs to insure the usability of the operator within
  374. the sargable clause with the index under consideration.
  375. For example, an area B-tree, whose records are sorted in \*(lqAREALT\*(rq order,
  376. can be used with sargable clauses for which \fIOPR\fR is:
  377. .(l
  378. AREAEQ, AREAGT, AREAGE, AREALT, or AREALE,
  379. .)l
  380. while a standard B-tree can be used with sargable clauses containing:
  381. .(l
  382. =, >, $>=$, <, or $<=$.
  383. .)l
  384. To distinguish the two cases, POSTGRES introduces the 
  385. notion of a \*(lqclass.\*(rq
  386. Associated with every class is a particular access method and the set of
  387. operators that can be used with it.
  388. For example, the class \*(lqintops\*(rq refers to a standard B-tree.
  389. The operators associated with it are:
  390. .(l
  391. =, >, $>=$, <, and $<=$.
  392. .)l
  393. The class \*(lqareaops\*(rq is an area B-tree.
  394. Therefore, the operators associated with it are:
  395. .(l
  396. AREAEQ, AREAGT, AREAGE, AREALT, and AREALE.
  397. .)l
  398. Moreover, another class \*(lqhashops\*(rq is a standard hash table that
  399. can only be used with \*(lq=\*(rq.
  400. This information is stored as specified in table 3.1.
  401. .(z
  402. .hl
  403. .sz \n(ep
  404. .TS
  405. center;
  406. |c||c|c|c|c|
  407. c ||c|c|c|c|.
  408. _
  409. AMOP    access-method    class    operator    $...$ other information $...$
  410. _
  411.     B-tree    intops    = 
  412.     B-tree    intops     <
  413.     B-tree    intops    \(<=
  414.     B-tree    intops    >
  415.     B-tree    intops    \(>=
  416.     B-tree    areaops    AREAEQ
  417.     B-tree    areaops    AREALT
  418.     B-tree    areaops    AREALE
  419.     B-tree    areaops    AREAGT
  420.     B-tree    areaops    AREAGE
  421.     hash    hashops    = 
  422.     _    _    _    _
  423. .TE
  424. .sz \n(pp
  425. .sp
  426. .ce 2
  427. \fBTable 3.1\fR
  428. Index and operator classes
  429. .hl
  430. .)z
  431. To determine the usability of an index, whose key matches the variable
  432. within a sargable clause, table 3.1
  433. is scanned using the class of the index and the operator within the
  434. sargable clause as a search key.
  435. If the pair is found, then the index can be used; otherwise, it cannot.
  436. .pp
  437. Determining index usability, therefore, involves matching operators that appear
  438. within sargable restriction clauses with the operators associated with
  439. an index's class.
  440. By requiring that qualifications be transformed so variables are 
  441. on the left-hand side and constants are on the right-hand side,
  442. the optimizer is insured that an operator appearing within a 
  443. clause is semantically equivalent
  444. to the actual operator that appears in table 3.1.
  445. For example, suppose the operator \*(lqfoo\*(rq is usable with index
  446. \*(lqfoobar,\*(rq but its commutator \*(lqbar\*(rq is not.
  447. If we have the following restriction:
  448. .(l
  449. 10 foo relation.field,
  450. .)l
  451. the optimizer should not consider using index \*(lqfoobar\*(rq because the above
  452. restriction is equivalent to:
  453. .(l
  454. relation.field bar 10.
  455. .)l
  456. .pp
  457. Currently, only a single clause can be used with an index defined on a single
  458. key.
  459. For example, if the following two clauses are contained in a query:
  460. .(l
  461. r.foo > 100 \fBand\fR r.foo < 150,
  462. .)l
  463. and a B-tree index is defined on the field \*(lqfoo,\*(rq then
  464. only one of the two clauses can be used with the index.
  465. In other words, either \fIall\fR values foo > 100 are located using the
  466. index, or \fIall\fR values < 150, but \fInot\fR both.
  467. Had both clauses been used with the index, only those values between
  468. 100 and 150 would have to be examined.
  469. This has the possibility of reducing the scope of an index
  470. scan substantially.
  471. However, such an optimization is being reserved for
  472. future extensions of POSTGRES.
  473. It will not only require a 
  474. a redefinition of the access method interface
  475. but also extra information from the user establishing which operator should
  476. be used at the low end of a scan (e.g., >) and which corresponds to
  477. the high end (e.g., <).
  478. The first requirement is necessary because currently the access
  479. method interface is only
  480. designed to work with one clause per index key.
  481. .pp
  482. In the case where an index is defined on multiple keys,
  483. the ideas discussed above simply generalize.
  484. In other words, for every key of an index, there must be a corresponding
  485. sargable restriction clause, and
  486. in addition to all operators in these
  487. clauses being usable by the index, they all must be identical.
  488. A more flexible approach would have allowed partial matching of keys
  489. and multiple operators to be used in a single scan.
  490. This would have required that POSTGRES store further information about operators
  491. and access methods in system catalogs.
  492. Namely, for every access method, the optimizer would need an indication 
  493. as to whether partial key matching is possible, and
  494. it also would need some way of establishing that the following
  495. clause: 
  496. .(l
  497. r.field1 = 1 \fBand\fR r.field2 > 5
  498. .)l
  499. can be used with a B-tree index defined on \*(lqfield1\*(rq
  500. and \*(lqfield2,\*(rq but the following cannot:
  501. .(l
  502. r.field1 > 1 \fBand\fR r.field2 < 10.
  503. .)l
  504. The benefits of this extra information is not significant
  505. enough to justify the extra complexity that would be
  506. required when defining new operators and access methods;
  507. therefore, POSTGRES does not implement these features.
  508. .pp
  509. One optimization that the POSTGRES planner does support is use of
  510. multiple indices to process \*(lqor\*(rq clauses.
  511. Normally, it would not be possible to use an index scan with the
  512. following clause:
  513. .(l
  514. r.field = 1 \fBor\fR r.field = 2
  515. .)l
  516. because there are two key values, 1 and 2.
  517. However, it is possible to use an index scan keyed on 1 followed by
  518. another index scan keyed on 2.
  519. Since two index scans may be much less expensive than a single sequential scan,
  520. the optimizer will consider using multiple index scans.
  521. .pp
  522. In addition to restricting the scope of a search, index paths are also
  523. considered for another reason.
  524. During query processing, it may be necessary to sort an intermediate
  525. result prior to a merge-sort join (see figure 3.2), or the user may
  526. specify that the results of
  527. a retrieve query be sorted on certain fields.
  528. However, these sorts do not have to performed explicitly at all times.
  529. Some access methods maintain their tuples sorted on the keys
  530. used to define the structure.
  531. Thus, scanning a relation via such an index may yield tuples sorted in a
  532. desired order.
  533. For example, a standard B-tree stores its tuples sorted either in
  534. ascending (<) or descending (>) order, while an area B-tree maintains its
  535. tuples sorted in either \*(lqAREALT\*(rq or \*(lqAREAGT\*(rq order.
  536. .pp
  537. To make use of an index with this sort characteristic,
  538. the index keys must either
  539. match variables within join clauses, which correspond to relations 
  540. that will later be merge-sorted, or attribute fields on which 
  541. a query's resulting tuples will be sorted.
  542. To determine whether an index's
  543. implicit sort order is that which is needed, POSTGRES requires
  544. that users specify an access method's sort order (if it exists) when defining a
  545. new access method.
  546. If the implicit ordering matches a desired ordering and the keys are usable,
  547. a path that takes advantage of the index will be considered.
  548. The next two subsections will elaborate on further uses of this sort
  549. information.
  550. .sh 3 "Join Paths"
  551. .pp
  552. Once all feasible paths have been found for scanning single relations,
  553. paths are found for joining relations.
  554. Joins are first considered between every two relations for which there
  555. exists a corresponding join clause.
  556. For example, for the following query:
  557. .(l
  558. \fBretrieve\fR (A.a, B.b, C.c) \fBwhere\fR A.d = B.e,
  559. .)l
  560. during the first level of join processing, the only pairs considered are:
  561. .(l
  562. A join B
  563. B join A
  564. .)l
  565. All feasible paths are found for processing joins between these relation
  566. pairs.
  567. Having done this, all paths are then found for processing 3-way joins,
  568. using available 2-way join paths for the outer
  569. path and relation scan paths for the inner path.
  570. Again, the optimizer only considers those join pairs for which there is
  571. a corresponding join clause.
  572. If this heuristic results in no further relations being joined,
  573. all remaining possibilities are considered.
  574. For the above query, at the second level of join processing, no relations
  575. should be joined according to the heuristic.
  576. Therefore, the remaining possibilities are:
  577. .(l
  578. (A join B) join C
  579. (B join A) join C
  580. .)l
  581. From here, these steps are repeated until no further join levels need
  582. to be processed.
  583. .pp
  584. All possible join paths are generated for every join pair considered.
  585. The simplest join strategy is nested iteration.
  586. In a nested iteration join, the inner join relation is scanned once
  587. for every tuple found in the outer join relation.
  588. All available paths on the outer join relation are possibilities
  589. for the outer path.
  590. On the other hand, since the inner join path is independent of
  591. the outer in a nested iteration join,
  592. only the least expensive path for the inner join relation is a
  593. possibility for the inner path.
  594. .pp
  595. Nested iteration is simple, but it can be a time-consuming join strategy,
  596. especially if the inner join relation is not indexed on join
  597. variables.
  598. A join strategy that is much more attractive in these situations is merge-sort.
  599. A merge-sort join can be used to process a join between
  600. \fIrelation1\fR and \fIrelation2\fR,
  601. provided there is a merge join clause of the form:
  602. .(l
  603. \fIrelation1.field1 OPR relation2.field2\fR.
  604. .)l
  605. During the first phase of a merge-sort, each relation is sorted on
  606. appropriate join attributes.
  607. During the second phase, the merge phase, the two relations 
  608. are merged together, taking
  609. advantage of the fact that both relations are ordered on join attributes.
  610. .pp
  611. For a merge-sort join to be advantageous,
  612. the operator within a merge join clause must be
  613. \*(lqsimilar to\*(rq an equality operator, e.g. \*(lqAREAEQ\*(rq.
  614. Therefore, in the most ideal situation, when both join relations contain
  615. unique values in the merge join fields, the merge phase will only require
  616. a sequential scan of both sorted relations.
  617. So when defining new operators, POSTGRES requires that users indicate
  618. whether an operator is \*(lqmergesortable\*(rq by specifying the
  619. operator that must be used to sort the two join relations prior to
  620. the merge.
  621. For example, \*(lq=\*(rq is mergesortable, provided the sort is made in
  622. \*(lq<\*(rq order, while \*(lqAREAEQ\*(rq is also mergesortable,
  623. provided the sort is in \*(lqAREALT\*(rq order.
  624. Therefore, if a join clause in the form specified above
  625. contains a mergesortable operator, then a merge-sort join
  626. path between the two relations in the clause is considered in addition to
  627. paths that process the join using nested iteration.
  628. .pp
  629. As alluded to earlier, relations may not always have to be explicitly sorted
  630. prior to a merge.
  631. If the keys of an index match join attributes and
  632. the index's implicit sort order
  633. matches the sort operator required for the merge,
  634. the relation need not be sorted;
  635. unless, of course, it is cheaper to scan a relation into a temporary
  636. via its least expensive path, sort the temporary, and then read the
  637. sort result.
  638. .pp
  639. A second join strategy that may be useful when indices are not available
  640. is hash-join.
  641. To process a join using this strategy, the inner join relation is first
  642. hashed on its join attributes.
  643. Then, the outer relation is scanned.
  644. For every tuple found, values within outer join attributes are used as
  645. hash keys to locate tuples in the inner join relation.
  646. Thus, to use such a strategy, the join operator also must be
  647. \*(lqsimilar to\*(rq an equality operator.
  648. Users will indicate this by simply specifying whether an operator
  649. is \*(lqhashjoinable\*(rq when defining a new operator.
  650. .sh 3 "Pruning The Plan Space"
  651. .pp
  652. In generating possible plans for a query, many paths are considered.
  653. In fact, the plan space is exponential because 
  654. plans at lower levels are used in creating plans at higher levels.
  655. Furthermore, when indices and multiple join strategies can be used,
  656. there are a number of ways of processing scans and joins on
  657. identical relations.
  658. Moreover, some of these paths may be redundant.
  659. .pp
  660. Two paths are redundant if they scan identical relations, and their
  661. resulting tuples are sorted on identical fields.
  662. The latter is determined by making use of
  663. index sort information.
  664. For example, suppose the outer relation of a join is scanned using an index
  665. that sorts its tuples in ascending order, and the relation is joined
  666. with another relation using nested iteration.
  667. This join path is equivalent to another path where
  668. the outer relation is explicitly sorted into ascending order and
  669. merge-sorted with the same inner join relation.
  670. Although these two paths are different, they will yield identical results
  671. because both outer join relations are sorted in identical order, and
  672. joins preserve the sort order of the outer relation.
  673. Therefore, after generating plans for each level of joins,
  674. if two redundant join plans are found, the optimizer can eliminate the
  675. more expensive of the two, thereby reducing the size
  676. of the plan space.
  677. .sh 2 "Estimating Costs and Sizes"
  678. .pp
  679. To prune the plan space as well as determine the optimal plan,
  680. the optimizer must estimate the cost of 
  681. executing every plan it generates.
  682. In the SYSTEM-R optimizer,
  683. both CPU and I/O time are accounted for in estimating costs.
  684. Every cost factor is of the form:
  685. .(l
  686. cost = P + W * T,
  687. .)l
  688. where \fIP\fR is the number of pages examined at runtime by a plan and
  689. \fIT\fR is the number of tuples examined.
  690. \fIP\fR reflects I/O cost, \fIT\fR reflects CPU cost,
  691. and \fIW\fR is a weighting factor that indicates the relative importance of
  692. I/O to CPU in terms of processing cost.
  693. Thus, for a sequential scan of a relation,
  694. since every page and tuple must be examined,
  695. \fIP\fR equals the number of
  696. pages in the relation and \fIT\fR equals the number of tuples.
  697. For a secondary index scan, the number of pages and tuples touched also
  698. depends on the number of pages and tuples in the index relation
  699. because index pages and tuples must be read first to determine where to
  700. scan in the main relation.
  701. .pp
  702. For every index,
  703. the number of pages and tuples touched is also determined by
  704. the fraction of tuples in a relation that one would expect to satisfy
  705. restriction clauses specified on the relation.
  706. This fractional quantity is called a \*(lqselectivity.\*(rq
  707. Selectivities are functions of a variety of parameters,
  708. including the operator in the restriction clause, the restriction constant,
  709. the number of records in an index, and the maximum and minimum values 
  710. of entries stored in an attribute.
  711. In SYSTEM-R, every possible selectivity factor is
  712. hardwired into the cost estimation code.
  713. See table 3.2 for a sampling of selectivity factors.
  714. .(z
  715. .hl
  716. .sz \n(ep
  717. .TS
  718. center box;
  719. c|c
  720. l|l.
  721. qualification    selectivity factor
  722. =
  723. r.field = value    1/(number of tuples in the index relation defined on r.field)
  724. _
  725. r.field > value    ((high value of r.field) - value) /
  726.     ((high value of r.field) - (low value of r.field))
  727. _
  728. r.field < value    (value - (low value of r.field))/
  729.     ((high value of r.field) - (low value of on r.field))
  730. .TE
  731. .sz \n(pp
  732. .sp
  733. .ce 2
  734. \fBTable 3.2\fR
  735. Examples of selectivity factors for SYSTEM-R
  736. .hl
  737. .)z
  738. .pp
  739. Cost estimation formulas for all join strategies are functions of the sizes,
  740. in pages and tuples, of the outer and inner join relations.
  741. To estimate the size of either an outer or inner join relation, the optimizer
  742. simply multiplies the original size of the relation by the selectivity
  743. of every restriction clause applicable to the relation.
  744. If the clause can be used with an index, the selectivity is computed 
  745. as described earlier.
  746. If it cannot, SYSTEM-R resorts to an \*(lqelse case,\*(rq
  747. associating constants with these factors.
  748. The SYSTEM-R designers justify this simplification by stating that if an index 
  749. is not defined on
  750. a relation, this implies that the relation is small; so if the
  751. selectivity is not accurate, the difference is insignificant.
  752. If the outer join relation is a composite relation, the desired
  753. selectivity is that of a join operation.
  754. Such a selectivity indicates the fraction
  755. from among the cross product of an outer and inner join relation
  756. one would expect to satisfy a join clause.
  757. Again, SYSTEM-R hardwires this information into the optimizer code.
  758. For a summary of the different cost formulas required, see table 3.3.
  759. .(z
  760. .hl
  761. .sz \n(ep
  762. .TS
  763. center box;
  764. c s s
  765. c|c|c
  766. l|l|l.
  767. Cost of Scans
  768. =
  769.     P    T
  770. _
  771. Sequential Scan    NumPages    NumTuples
  772. _
  773. Primary Index Scan    NumPages*F    NumTuples*F
  774. _
  775. Secondary Index Scan    NumPages*F + ITuples*F    ITuples*F + NumTuples*F
  776. .TE
  777. .sp
  778. \fIwhere\fR
  779. .in +0.5i
  780. .nf
  781. NumPages = the number of pages in a relation
  782. NumTuples = the number of tuples in a relation
  783. ITuples = the number of tuples in an index relation
  784. F = combined selectivity factor of applicable restriction clauses
  785. .fi
  786. .in -0.5i
  787. .sp
  788. .ce
  789. .TS
  790. center box;
  791. c s
  792. l|l.
  793. Cost of Joins
  794. =
  795. Nested Iteration    $C sub outer + N sub outer * C sub inner$
  796. _
  797. Merge-sort    $C sub outer + C sub sortouter + C sub inner + C sub sortinner$
  798. _
  799. Hash-join    $C sub outer + C sub createhash + N sub outer * C sub hash$
  800. .TE
  801. .sp
  802. \fIwhere\fR
  803. .in +0.5i
  804. .nf
  805. $C sub outer$ = the cost of scanning the outer join relation
  806. $C sub inner$ = the cost of scanning the inner join relation
  807. $C sub sortouter$ = the cost of sorting the outer join relation into a temporary$" " sup \(dg$
  808. $C sub sortinner$ = the cost of sorting the inner join relation into a temporary$" " sup \(dg$
  809. $C sub createhash$ = the cost of hashing the inner join relation into a temporary
  810. $C sub hash$ = the cost of a single hash
  811. $N sub outer$ = the size of the outer join relation
  812. .fi
  813. .in -0.5i
  814. .sp 2
  815. .nf
  816. .sz \n(fp
  817. $" " sup \(dg$ equals 0 if sort is not required
  818. .sz \n(pp
  819. .fi
  820. .sp
  821. .ce 2
  822. \fBTable 3.3\fR
  823. Summary of cost formulas
  824. .hl
  825. .)z
  826. .pp
  827. The POSTGRES optimizer uses this same basic idea in estimating
  828. costs.
  829. As in SYSTEM-R, the system stores statistical information that cost formulas
  830. depend upon in database system catalogs.
  831. However, POSTGRES takes the novel approach of updating certain
  832. statistics, like
  833. the number of pages and tuples in relations, using demons, which execute
  834. in background mode.
  835. These demons are implemented using triggers, a feature
  836. POSTGRES supports.
  837. Another new idea is that other statistics, e.g., the high and low values 
  838. of an attribute, are stored in the form of queries embedded within
  839. data fields.
  840. These queries will retrieve appropriate information from elsewhere
  841. in the database, and the system will cache the result so the optimizer
  842. need not repeatedly execute the same query.
  843. These two provisions alleviate problems other systems encountered when
  844. updating database statistics.
  845. INGRES updated statistics immediately, resulting in loss of concurrency
  846. because it blocked other
  847. users access to information in system catalogs.
  848. SYSTEM-R chose not to update statistics at runtime, instead requiring
  849. that a data base administrator run a special command to explicitly do
  850. the update.
  851. This, however, meant that statistics were not always up-to-date,
  852. possibly yielding incorrect cost estimations.
  853. .pp
  854. To account for all possible selectivities, the approach of storing
  855. information in catalogs is used again.
  856. One difference between selectivity information and other operator and 
  857. access method
  858. information seen thus far is that selectivities are parameter
  859. dependent, as illustrated in table 3.2.
  860. An earlier approach suggested storing simple formulas within data fields
  861. and writing a parser to interpret the formula, substituting appropriate
  862. values for a fixed set of possible parameters [STON85a].
  863. This is fairly straightforward, but not very flexible.
  864. Instead, our optimizer capitalizes on an inherent feature of POSTGRES.
  865. As already mentioned, POSTGRES supports
  866. embedding of queries within data fields.
  867. Generalizing on this idea, POSTGRES also allows users to define
  868. arbitrary procedures 
  869. written in general purpose programming languages, like C and LISP,
  870. which can then be registered into the system and
  871. stored within data fields [STON86c].
  872. Therefore, in POSTGRES every selectivity formula is expressed
  873. using a parameterized procedure.
  874. Each procedure accepts as its arguments the relations, 
  875. attributes, and constants
  876. appearing within restrictions and the index identifier and number of index
  877. keys, if an index is used.
  878. The routine then retrieves necessary information
  879. from elsewhere in the database and returns a computed selectivity.
  880. .pp
  881. As discussed in reference to SYSTEM-R selectivities, selectivities come
  882. in three flavors.
  883. Therefore, there will be a selectivity routine associated with every feasible
  884. operator-class pair shown in table 3.1, and every operator will have two
  885. selectivitiy routines associated with it \*- one for
  886. ordinary restrictions, which are not used with index scans,
  887. and the other for joins.
  888. Each of these procedures is stored within appropriate tuple entries.
  889. .pp
  890. Thus, by executing the appropriate procedure with the appropriate parameters,
  891. selectivity computation is flexible and simple.
  892. A procedure written in pseudo C code that computes the selectivity of
  893. the operator \*(lq>\*(rq for a B-tree index is shown in figure 3.6.
  894. .(z
  895. .hl
  896. .(l
  897. .sz \n(ep
  898. .ft I
  899. /*
  900.  *    Procedure to compute the selectivity of the \*(lq>\*(rq operator 
  901.  *    when it is used with a B-tree index defined on integer fields.
  902.  */
  903. .ft R
  904.  
  905. float
  906. greater_btree (opid, relid, attnos, values, flags, indexid, nkeys)
  907. int    opid;        \fI/* contains unique id of operator \*(lq>\*(rq */\fR
  908. int    relid;
  909. int    attnos[];
  910. int    values[];
  911. int    flags[];        \fI/* equals 1 if clause is of the form `var > constant,'
  912.              * else clause is of the form `constant > var'
  913.              */\fR
  914. int    indexid;    \fI/* parameter isn't used by this particular routine */\fR
  915. int    nkeys;
  916. {
  917.     int    i;
  918.     int    high;
  919.     int    low;
  920.     float    s;
  921.  
  922.     s = 1.0;
  923.     \fBfor\fR (i = 0; i < nkeys; ++i) {
  924.         high = retrieve high value of attribute `attnos[i]' in relation `relid';
  925.         low = retrieve low value of attribute `attnos[i]' in relation `relid';
  926.         \fI/*
  927.          * the selectivity of multiple clauses is the product of the
  928.          * selectivity of each individual clause
  929.          */\fR
  930.         \fBif\fR (flags[i] == 1)
  931.             s = s * (high - values[i]) / (high - low);
  932.         \fBelse\fR
  933.             s = s * (values[i] - low) / (high - low);
  934.     }
  935.     \fBreturn\fR(s);
  936. }
  937. .sz \n(pp
  938. .)l
  939. .sp
  940. .ce 2
  941. \fBFigure 3.6\fR
  942. Code to compute selectivity
  943. .hl
  944. .)z
  945. .sh 2 "Nested-attribute Queries"
  946. .pp
  947. The last several subsections have described optimization of simple queries,
  948. i.e. those without nested attributes.
  949. Figure 3.7 summarizes information the optimizer uses in generating
  950. possible query plans.
  951. .(z
  952. .hl
  953. .sz \n(fp
  954. .TS
  955. center;
  956. |c||c|c|c|c|c|c|c|
  957. c ||c|c|c|c|c|c|c|.
  958. _
  959. OPER    operator    negator    commutator    msortop    hash    selectivity    join-selec
  960. _
  961.     =     \(!=    =     <    yes    \fIprocedures\fR    \fIprocedures\fR
  962.     <    \(>=    >    no    no    \fIto compute\fR    \fIto compute\fR
  963.     \(<=    >    \(>=    no    no    \fIselectivity\fR    \fIselectivity\fR
  964.     >    \(<=    <    no    no    \fIof\fR    \fIof\fR
  965.     \(>=    <    \(<=    no    no    \fI1-variable\fR    \fIjoin\fR
  966.     AREAEQ    AREANEQ    AREAEQ    AREALT    yes    \fIclauses\fR    \fIclauses\fR
  967.     AREALT    AREAGE    AREAGT    no    no    \fIcontaining\fR    \fIcontaining\fR
  968.     AREALE    AREAGT    AREAGE    no    no    \fI\*(lqoperator\*(rq\fR    \fI\*(lqoperator\*(rq\fR
  969.     AREAGT    AREALE    AREALT    no    no
  970.     AREAGE    AREALT    AREALE    no    no
  971.     _    _    _    _    _    _    _
  972. .TE
  973. .sp 2
  974. .TS
  975. center;
  976. |c||c|c|c|c|c|
  977. c ||c|c|c|c|c|.
  978. _
  979. AMOP    access-method    class    operator    selectivity    strategy \(dg
  980. _
  981.     B-tree    intops    =     \fIprocedures\fR    = 
  982.     B-tree    intops    <    \fIto compute\fR    <
  983.     B-tree    intops    \(<=    \fIselectivity\fR    \(<=
  984.     B-tree    intops    >    \fIof clauses\fR    >
  985.     B-tree    intops    \(>=    \fIcontaining\fR    \(>=
  986.     B-tree    areaops    AREAEQ    \fI\*(lqoperator\*(rq\fR    = 
  987.     B-tree    areaops    AREALT    \fIwhen used\fR    <
  988.     B-tree    areaops    AREALE    \fIwith index\fR    \(<=
  989.     B-tree    areaops    AREAGT    \fI\*(lqclass\*(rq\fR    >
  990.     B-tree    areaops    AREAGE        \(>=
  991.     hash    hashops    =         = 
  992.     B-tree    intops    <        sort
  993.     B-tree    areaops    AREALT        sort
  994.     _    _    _    _    _
  995. .TE
  996. .sp
  997. .nf
  998. $" " sup \(dg$ used in determining which operator corresponds to each generic operation (e.g., sorting)
  999. .fi
  1000. .sz \n(pp
  1001. .sp
  1002. .ce 2
  1003. \fBFigure 3.7\fR
  1004. Summary of optimizer information stored in system catalogs
  1005. .hl
  1006. .)z
  1007. .pp
  1008. From here on, the module implementing the algorithms just described
  1009. will be called
  1010. the \*(lqsubplanner,\*(rq while the entire optimizer will be labeled the
  1011. \*(lqplanner\*(rq.
  1012. To create access plans for queries containing nested attributes,
  1013. the planner simply applies the subplanner algorithm
  1014. once for each nesting level of attributes in a query.
  1015. In other words, for any query, the number of times the subplanner is called
  1016. is equal to the maximum nesting of attributes in the query.
  1017. Once all subplanner calls have completed, the planner then builds a final 
  1018. plan that indicates how these subpieces fit together.
  1019. Thus, given a query, the planner first modifies it to 
  1020. consider only top level attributes.
  1021. This new query is passed to the subplanner to create a \fIsubplan\fR.
  1022. The planner then modifies the original query to consider only nested attributes.
  1023. This is recursively processed by the planner
  1024. to create a \fIplan\fR, and
  1025. the resulting access plan simply indicates which attributes
  1026. from \fIsubplan\fR and \fIplan\fR should be returned to the user.
  1027. .pp
  1028. An example will illustrate these ideas more clearly.
  1029. Suppose we have the following relation:
  1030. .(l
  1031. EMP ( name, dept, hobbies ),
  1032. .)l
  1033. where hobbies contains POSTQUEL queries to retrieve information about
  1034. the different hobbies each employee participates in.
  1035. One of these relations may be:
  1036. .(l
  1037. SOFTBALL ( empname, position, batting-history ),
  1038. .)l
  1039. where batting-history contains a POSTQUEL query retrieving information
  1040. about an employee's past batting averages from the relation:
  1041. .(l
  1042. BATTING-HIST ( empname, year, avg ).
  1043. .)l
  1044. Given the following query:
  1045. .(l
  1046. \fIQ1\fR: \fBretrieve\fR (EMP.name, EMP.hobbies.batting-history.avg) \fBwhere\fR
  1047.     EMP.hobbies.batting-history.year = \*(lq1986\*(rq \fBand\fR
  1048.     EMP.hobbies.position = \*(lqcatcher\*(rq \fBand\fR
  1049.     EMP.dept = DEPT.dname \fBand\fR
  1050.     DEPT.floor = 1,
  1051. .)l
  1052. which finds the current batting average of employees who are catchers and work
  1053. on the first floor,
  1054. the planner will first consider the top level of attributes by passing
  1055. the following query to subplanner:
  1056. .(l
  1057. \fIS1\fR: \fBretrieve\fR (EMP.name, EMP.hobbies) \fBwhere\fR
  1058.     EMP.dept = DEPT.dname \fBand\fR
  1059.     DEPT.floor = 1.
  1060. .)l
  1061. After the subplanner has optimized the above query,
  1062. the planner then only considers attributes beyond the top level nesting:
  1063. .(l
  1064. \fIQ2\fR: \fBretrieve\fR (EMP.hobbies.batting-history.avg) \fBwhere\fR
  1065.     EMP.hobbies.batting-history.year = \*(lq1986\*(rq \fBand\fR
  1066.     EMP.hobbies.position = \*(lqcatcher\*(rq
  1067. .)l
  1068. and passes this query to itself recursively.
  1069. This process is repeated, and in the process, the subplanner optimizes the 
  1070. following two queries:
  1071. .(l
  1072. \fIS2\fR: \fBretrieve\fR (EMP.hobbies.batting-history) \fBwhere\fR
  1073.     EMP.hobbies.position = \*(lqcatcher\*(rq
  1074.  
  1075. \fIS3\fR: \fBretrieve\fR (EMP.hobbies.batting-history.avg) \fBwhere\fR
  1076.     EMP.hobbies.batting-history.year =\*(lq1986\*(rq
  1077. .)l
  1078. Since the maximum attribute nesting is three, recursion ends.
  1079. The final plan is constructed as shown in figure 3.8, where \fIPn\fR
  1080. is a subplan for executing subquery \fISn\fR. 
  1081. \fIR2\fR is a node corresponding to \fIQ2\fR that indicates that
  1082. EMP.hobbies.batting-history.avg should be retrieved from \fIP3\fR,
  1083. and \fIR1\fR corresponds to \fIQ1\fR, indicating
  1084. that EMP.name should be retrieved from \fIP1\fR and
  1085. EMP.hobbies.batting-history.avg originates from \fIR2\fR.
  1086. .(z
  1087. .hl
  1088. .GS
  1089. file figure3.8
  1090. .GE
  1091. .sp
  1092. .ce 2
  1093. \fBFigure 3.8\fR
  1094. Structure of a query plan
  1095. .hl
  1096. .)z
  1097. .pp
  1098. To process this plan, the query executor first executes \fIP1\fR.
  1099. As soon as execution of \fIP1\fR returns a single tuple, \fIT\fR,
  1100. EMP.hobbies in that tuple is materialized into a temporary relation, 
  1101. if that has not
  1102. already been done by POSTGRES's facility for preexecuting embedded queries.
  1103. The executor then processes the subtree whose root is \fIR2\fR in
  1104. a recursive manner for that instance of EMP.hobbies.
  1105. Each EMP.hobbies.batting-history.avg value returned from this 
  1106. execution is combined
  1107. with EMP.name from \fIT\fR to create a result tuple that is passed to the
  1108. user.
  1109. When execution of \fIR2\fR has completed, \fIP1\fR is reexecuted to retrieve
  1110. another EMP.hobbies value that is used to process another instance 
  1111. of the \fIR2\fR subtree.
  1112. This is repeated until all tuples from \fIP1\fR have been found.
  1113. The subtree \fIR2\fR is processed similarly.
  1114. \fIP2\fR is processed first, and \fIP3\fR is processed once for each
  1115. instance of a qualifying \fIP2\fR tuple.
  1116. .pp
  1117. Processing nested-attribute queries in this way is attractive because
  1118. it only requires a very clean extension of the basic optimization algorithm,
  1119. and nested attributes are transparent to the subplanner.
  1120. As a result, the code for query processing is also simpler.
  1121. Furthermore, entire query plans are generated a priori, eliminating the
  1122. need to optimize subqueries at runtime.
  1123. .pp
  1124. Optimizing nested-attribute queries a priori, however, does suffer because of 
  1125. its simplicity.
  1126. In generating plans for higher nesting levels, the contents of relations that
  1127. will be
  1128. materialized from embedded queries are not known in advance.
  1129. Therefore, the optimizer does not know the sizes of these relations or the
  1130. distribution of values within nested attributes;
  1131. this information or some estimate
  1132. is normally required in computing subplan costs.
  1133. To work around this problem, the optimizer
  1134. simply assumes that materialized relations are
  1135. all of the same size, and if a nested attribute appears within a clause,
  1136. rather than return a parameter-dependent value for the selectivity,
  1137. the selectivity code
  1138. instead returns a constant that reflects the relative selectivities of various
  1139. operators.
  1140. For example, \*(lq=\*(rq is more selective than \*(lq>.\*(rq
  1141. So a clause containing \*(lq=\*(rq may have a selectivity of
  1142. $1 over 10$, while a clause with \*(lq>\*(rq
  1143. has a selectivity of $1 over 4$.
  1144. Although this may be an oversimplification, usually relations
  1145. materialized from embedded queries will be small.
  1146. So if the resulting plan is not the overall best choice in actuality,
  1147. the chosen plan will not be a bad plan.
  1148. .pp
  1149. As an alternative, pieces of nested-attribute queries can be optimized
  1150. at runtime.
  1151. This can be advantageous not only because relation sizes and attribute
  1152. distributions are known, but also
  1153. because it enables the optimizer to consider special paths.
  1154. For example, it may be possible to process an embedded query using
  1155. a B-tree index that sorts its records in ascending order.
  1156. If the tuples materialized from this query are later merge-sorted 
  1157. using an ascending sort, due to the available index path,
  1158. the query processor need not explicitly sort the relation.
  1159. A runtime optimizer would be able to note this.
  1160. .pp
  1161. Although more intelligent query plans are generated,
  1162. there is a great deal of planning overhead associated with runtime
  1163. optimization.
  1164. For every tuple generated by \fIP1\fR, a subquery of the form:
  1165. .(l
  1166. \fBretrieve\fR (TEMP.batting-history.avg) \fBwhere\fR
  1167.     TEMP.batting-history.year = \*(lq1986\*(rq \fBand\fR
  1168.     TEMP.position = \*(lqcatcher\*(rq
  1169. .)l
  1170. must be optimized, where TEMP is the relation materialized from EMP.hobbies.
  1171. Subsequently, for every tuple generated by the above query, the following
  1172. query:
  1173. .(l
  1174. \fBretrieve\fR ($TEMP prime$.avg) \fBwhere\fR
  1175.     $TEMP prime$.year = \*(lq1986\*(rq
  1176. .)l
  1177. must also be optimized, where $TEMP prime$ is materialized from
  1178. TEMP.batting-history.
  1179. Due to this extra overhead, the efficiency of runtime
  1180. optimization is questionable.
  1181. .sh 2 "Query Plans"
  1182. .pp
  1183. The plan created by the optimizer is a tree of nodes.
  1184. Each node
  1185. corresponds to some scan, join, sort, or hash, or creation of a subresult.
  1186. Scan and join nodes contain information indicating which attributes
  1187. should be retrieved, which qualifications must be satisfied, and any other
  1188. information relevant to the particular type of scan or join.
  1189. Sort and hash nodes indicate which attributes should be placed into a
  1190. temporary and the operator used to perform the sort or hash.
  1191. A subresult node interconnects subplans and plans, indicating which attributes
  1192. should be retrieved and from where.
  1193. As an optimization, the topmost result node contains constant
  1194. qualifications, i.e. those without variables, so these clauses
  1195. can be examined prior to any other processing.
  1196. .pp
  1197. A possible (not necessarily the optimal) plan for the query introduced
  1198. in the previous subsection is shown in figure 3.9.
  1199. .(z
  1200. .hl
  1201. .GS
  1202. file figure3.9
  1203. .GE
  1204. .sp
  1205. .ce 2
  1206. \fBFigure 3.9\fR
  1207. Sample query plan tree
  1208. .hl
  1209. .)z
  1210. There are a few things about the tree that should be elaborated on to avoid
  1211. misconceptions.
  1212. First of all, the materialization steps shown
  1213. are not executed at runtime if the necessary embedded queries 
  1214. have already been preexecuted, and their results remain valid.
  1215. Furthermore, as will become apparent later, these materialization steps
  1216. are actually implicit in the plan tree.
  1217. .pp
  1218. From the figure, it would appear that throughout the plan tree,
  1219. relation entries are explicitly
  1220. identified using relation and attribute names.
  1221. This would imply that the query processor would have to match identifiers
  1222. to locate values that originate from nodes elsewhere in the tree.
  1223. For example, references to TEMP1 and EMP attributes in the mergesort node
  1224. would be found by searching for an appropriate identifier within tuples
  1225. that originate from the two nodes below the mergesort node.
  1226. This, however, is not the case.
  1227. Explicit references are shown only for readability.
  1228. Rather, an attribute is identified by its
  1229. relative position within a specified tuple.
  1230. By using this relative value and a descriptor associated with tuples
  1231. returned by each node, the query
  1232. processor can locate a desired attribute instantaneously.
  1233. .pp
  1234. The names of temporaries associated with materialized relations, again,
  1235. are shown only for readability.
  1236. In the actual plan tree, these relations are identified by the attribute
  1237. containing the queries that will be used to build the temporary.
  1238. For example, TEMP2 would be identified by the relative attribute identifier
  1239. of TEMP1.hobbies, while TEMP3 would be identified by TEMP2.batting-history.
  1240. By examining the contents of these referenced attribute entries, the query
  1241. executor can determine whether materialization is necessary.
  1242. Thus, the materialization step is implicit in these attribute references.
  1243. .pp
  1244. For temporaries associated with sorts and hashes,
  1245. the name of the temporary shown in the tree serves merely as an
  1246. identifier.
  1247. It is left to the runtime executor to create the actual name.
  1248.